package cn.bob.basic.service.impl;

import cn.bob.basic.config.BussinessException;
import cn.bob.basic.constant.BaseConstant;
import cn.bob.basic.dto.LoginDto;
import cn.bob.basic.dto.RegisterDto;
import cn.bob.basic.dto.WechatDto;
import cn.bob.basic.jwt.JwtUtils;
import cn.bob.basic.jwt.RsaUtils;
import cn.bob.basic.jwt.UserInfo;
import cn.bob.basic.service.ILoginService;
import cn.bob.basic.utils.AjaxResult;
import cn.bob.basic.utils.HttpUtil;
import cn.bob.basic.utils.MD5Utils;
import cn.bob.basic.utils.StrUtils;
import cn.bob.org.domain.Employee;
import cn.bob.org.mapper.EmployeeMapper;
import cn.bob.system.domain.Menu;
import cn.bob.system.mapper.MenuMapper;
import cn.bob.system.mapper.PermissionMapper;
import cn.bob.user.domain.Logininfo;
import cn.bob.user.domain.User;
import cn.bob.user.domain.Wxuser;
import cn.bob.user.mapper.LogininfoMapper;
import cn.bob.user.mapper.UserMapper;
import cn.bob.user.mapper.WxuserMapper;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.security.PrivateKey;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author bob
 * @data 2022/10/21-12:02
 */
@Service
@Slf4j
public class LoginServiceImpl implements ILoginService {
    @Autowired
    private LogininfoMapper logininfoMapper;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private WxuserMapper wxuserMapper;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private MenuMapper menuMapper;
    @Autowired
    private PermissionMapper permissionMapper;
    @Autowired
    private EmployeeMapper employeeMapper;

    @Value("${jwt.rsa.pri}")
    private String privateRsaKey;
    /**
     * 用户名/手机号/邮箱登录findUsernameToType  type=1
     * @param loginDto
     * @return
     */
    @Override
    public Map<String, Object> account(LoginDto loginDto) {
        Logininfo logininfo = getLoginPwd(loginDto);
        //账号密码都相同
        //获取token
        return getStringObjectHashMap(logininfo);
    }
    /**
     * 手机验证码登录
     * @param smsCodeLoginDto
     * @return
     */
    @Override
    public Map<String, Object> smsCodeLoginaccount(RegisterDto smsCodeLoginDto) {
        //判断logininfo中是否存在
        Logininfo logininfo = getPhoneSms(smsCodeLoginDto);
        //返回 token  logininfo
        return getStringObjectHashMap(logininfo);

    }

    /**
     * 微信接口通过接口获取token
     * @param wechatDto
     * @return
     */
    @Override
    public AjaxResult wechat(WechatDto wechatDto) {
        if (StringUtils.isBlank(wechatDto.getCode())){
            throw new BussinessException("code码不能为空！！");
        }
        //拼接获取token地址
        String token_uri=String.format(BaseConstant.VerifyWechat.PHONE_REDIS_TOKEN_URI,
                BaseConstant.VerifyWechat.PHONE_REDIS_APPID,
                BaseConstant.VerifyWechat.PHONE_REDIS_SECERT,wechatDto.getCode());
        //调用工具类获取到json格式字符
        String jsonstr = HttpUtil.httpGet(token_uri);
        //转换为json对象
        JSONObject jsonObject = JSON.parseObject(jsonstr);
        //获取到jsonobject中的access_token
        String accessToken = jsonObject.getString("access_token");
        //获取到jsonobject中的opneid
        String openid = jsonObject.getString("openid");
        //通过openid查询wxusermapper中是否存在数据
        Wxuser wxuser = wxuserMapper.findOpenid(openid);
        //wxuser存在数据再判断user是否存在用户    是否跳转绑定页面
        if (wxuser!=null){
            //用户存在直接返回token和logininfo信息
           Logininfo logininfo = logininfoMapper.findWxUser(wxuser.getUserId());
           if (logininfo!=null){
               //保存到redis中
               Map<String, Object> map = getStringObjectHashMap(logininfo);
               return AjaxResult.me().success(map);
           }
        }
        //不存在
        // 跳转到bind页面 拼接数据
        StringBuffer sb=new StringBuffer()
                .append("?accessToken=")
                .append(accessToken)
                .append("&openid=")
                .append(openid);
        return AjaxResult.me().fail("nobinder").setResultObj(sb);
    }

    /**
     * 微信绑定
     * @param wechatDto
     * @return
     */
    @Override
    public Map<String,Object> wechatbinder(WechatDto wechatDto) {
        if (StringUtils.isBlank(wechatDto.getPhone())) {
            throw new BussinessException("账号不能为空！！");
        }
        //验证码校验
        String format = (String) redisTemplate.opsForValue().get(String.format(BaseConstant.VerifyWechat.PHONE_REDIS_VERIFYCODE, wechatDto.getPhone()));
        if (StringUtils.isBlank(format)){
            throw new BussinessException("验证码错误！！！");
        }
        String[] split = format.split(":");
        if (!wechatDto.getVerifyCode().equals(split[0])){
            throw new BussinessException("验证码错误！！");
        }
        //获取到微信用户数据
        String wxu = String.format(BaseConstant.VerifyWechat.PHONE_REDIS_DATA, wechatDto.getAccessToken(), wechatDto.getOpenId());
        String wxdata = HttpUtil.httpGet(wxu);
        JSONObject wxjson = JSON.parseObject(wxdata);
        Wxuser wxuser = JSON.parseObject(wxdata, Wxuser.class);

        //根据手机查询用户
        User byuser = userMapper.findByPhone(wechatDto.getPhone());
        //创建登录用户
        Logininfo logininfo =  logininfoMapper.findWxUser(byuser.getId());

        //要绑定的数据不为空这个用户绑定到wxuser
        if (byuser==null){
            //将dto转换为user
            byuser.setUsername(wxuser.getNickname());
            byuser=dtoConvertUser(wechatDto);
            //将user转换为logininfo
            logininfo=userConvertlogin(byuser);
            logininfo.setType(1);
            logininfoMapper.add(logininfo);

            //创建用户
            byuser.setLogininfoId(logininfo.getId());
            userMapper.add(byuser);

        }
        wxuser.setUserId(byuser.getId());
        wxuserMapper.add(wxuser);
         //调用方法传到前端
        return getStringObjectHashMap(logininfo);
    }



    //将user转换为Logininfo
    public Logininfo userConvertlogin(User user){
        Logininfo logininfo = new Logininfo();
        BeanUtils.copyProperties(user,logininfo);
        return logininfo;
    }

    //将PhoneEmailDto转换为user
    public User dtoConvertUser(WechatDto dto){
        //获取盐值
        String salt = StrUtils.getComplexRandomString(32);
        String p="1";
        String pwd = MD5Utils.encrypByMd5(salt + p);
            return User.builder()
                    .username(dto.getPhone())
                    .phone(dto.getPhone())
                    .salt(salt)
                    .password(pwd)
                    .build();
    }

    //logininfo用户是否存在的校验 用户名 手机号 邮箱  密码的校验
    private Logininfo getLoginPwd(LoginDto loginDto) {
        //账号不能为空
        if (StringUtils.isBlank(loginDto.getPassword())){
            throw new BussinessException("账号不能为空！！");
        }
        //密码不能为空
        if (StringUtils.isBlank(loginDto.getUsername())){
            throw new BussinessException("密码不能为空！！");
        }
        Logininfo  logininfo=new Logininfo();
        //判断logininfo中是否存在      //前台前端登录 和后台前端登录
        if (loginDto.getType() ==1){
            logininfo= logininfoMapper.findUsernameToType(loginDto);
        } else {
            logininfo= logininfoMapper.findVueLogin(loginDto);
        }
        if (logininfo==null){
            throw new BussinessException("账号或密码错误！！");
        }
        //判断密码与加盐后的密码是否一致
        String salt = logininfo.getSalt();
        String password = loginDto.getPassword();
        String pwd = MD5Utils.encrypByMd5(salt + password);
        if (!logininfo.getPassword().equals(pwd)){
            throw new BussinessException("账号或密码错误！！");
        }
        return logininfo;
    }

    //logininfo用户是否存在的校验  手机短信验证码
    private Logininfo getPhoneSms(RegisterDto smsCodeLoginDto) {

        Logininfo  logininfo=logininfoMapper.findSmsCodeLogin(smsCodeLoginDto);
        if (logininfo==null){
            throw new BussinessException("账号未注册！！");
        }

        //判断输入的验证码是否一致
        String sms = (String) redisTemplate.opsForValue().get(String.format(BaseConstant.VerifySmsCodeCons.PHONE_REDIS_KEY, smsCodeLoginDto.getPhone()));
        System.out.println(sms);
        String[] split = sms.split(":");
        if (!smsCodeLoginDto.getSmsCode().equals( split[0])){
            throw new BussinessException("验证码错误！！");
        }
        return logininfo;
    }

    //修改登录传回的数据通过jwt进行传回数据
    private Map<String, Object> getStringObjectHashMap(Logininfo logininfo) {
        UserInfo userInfo = new UserInfo();
        //将用户信息中的敏感信息置空
        logininfo.setPhone("");
        logininfo.setSalt("");
        logininfo.setPassword("");
        //将logininfo数据添加
        userInfo.setLogininfo(logininfo);
        //进行判断为员工才进行添加
        if (0==logininfo.getType()){
            //查询添加menus
            List<Menu> menus=getMenuByLogId(logininfo.getId());
            userInfo.setMenus(menus);
            //查询添加permissions
            List<String> permissions= permissionMapper.findByLogininfoId(logininfo.getId());
            userInfo.setPermissions(permissions);
        }
        // 3.根据密钥文件名称获取到密钥
        PrivateKey privateKey = RsaUtils.getPrivateKey(this.getClass().getClassLoader().getResource(privateRsaKey).getFile());
        // 4.根据密钥、用户信息、获取JWT加密串
        String token = JwtUtils.generateTokenExpireInMinutes(userInfo, privateKey, 3000);
        // 5.按照正常逻辑我们直接返回token给前端即可，但是那样前端需要解密，我们使用简化版本JWT，还是返回用户信息
        // 封装token、用户信息、权限信息、菜单信息给前端
        Map<String, Object> map = new HashMap<>();
        //保存到map集合中
        map.put("token", token);
        map.put("logininfo", logininfo);
        map.put("menus", userInfo.getMenus());
        map.put("permissions", userInfo.getPermissions());

        return map;
    }

    public List<Menu> getMenuByLogId(Long id) {
        //通过传入的logininfoid查询menu的上级信息
        //通过logininfo找到emp表
        Employee emp = employeeMapper.findByLogid(id);
        //通过emp的id找到中间表对应的role集合
        List<Long> empMidden = employeeMapper.findEmpMidden(emp.getId());
        HashSet<Menu> menus = new HashSet<>();
        //找菜单
        for (Long aLong : empMidden) {
            List<Menu> middenMenuId = menuMapper.getMiddenMenuId(aLong);
            menus.addAll(middenMenuId);
        }
        //获取父类的菜单集合
        List<Menu> faterlist =  menuMapper.findAll().stream().filter(menu -> menu.getParentId() == null).collect(Collectors.toList());
        for (Menu fmenu : faterlist) {
            List<Menu> children = menus.stream().filter(menu -> menu.getParentId() == fmenu.getId()).collect(Collectors.toList());
            fmenu.setChildren(children);
        }
        //清除子集合为空的父类
        List<Menu> collect = faterlist.stream().filter(menu -> menu.getChildren().size()!=0).collect(Collectors.toList());
        return collect;
    }
//    //存入到redis的toke域logininfo信息
//    private Map<String, Object> getStringObjectHashMap(Logininfo logininfo) {
//        //账号密码都相同
//        //获取token
//        String token = UUID.randomUUID().toString();
//
//        //token和登录用户的信息都保存到redis中 生命周期30分钟
//        redisTemplate.opsForValue().set(token, logininfo, 30, TimeUnit.MINUTES);
//
//        //将用户信息中的敏感信息置空
//        logininfo.setPhone("");
//        logininfo.setSalt("");
//        logininfo.setPassword("");
//
//        Map<String, Object> map = new HashMap<>();
//        //保存到map集合中
//        map.put("token", token);
//        map.put("logininfo", logininfo);
//
//        return map;
//    }

}
